{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-02-13T08:28:06.727371Z",
     "start_time": "2021-02-13T08:28:04.925338Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Populating the interactive namespace from numpy and matplotlib\n"
     ]
    }
   ],
   "source": [
    "%pylab inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Notebook magic"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-02-01T17:37:25.505687Z",
     "start_time": "2022-02-01T17:37:25.493850Z"
    }
   },
   "outputs": [],
   "source": [
    "from IPython.core.magic import Magics, magics_class, line_cell_magic\n",
    "from IPython.core.magic import cell_magic, register_cell_magic, register_line_magic\n",
    "from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring\n",
    "import subprocess\n",
    "import os"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-02-01T17:37:25.717714Z",
     "start_time": "2022-02-01T17:37:25.532299Z"
    }
   },
   "outputs": [],
   "source": [
    "@magics_class\n",
    "class PyboardMagic(Magics):\n",
    "    @cell_magic\n",
    "    @magic_arguments()\n",
    "    @argument('-skip')\n",
    "    @argument('-unix')\n",
    "    @argument('-pyboard')\n",
    "    @argument('-file')\n",
    "    @argument('-data')\n",
    "    @argument('-time')\n",
    "    @argument('-memory')\n",
    "    def micropython(self, line='', cell=None):\n",
    "        args = parse_argstring(self.micropython, line)\n",
    "        if args.skip: # doesn't care about the cell's content\n",
    "            print('skipped execution')\n",
    "            return None # do not parse the rest\n",
    "        if args.unix: # tests the code on the unix port. Note that this works on unix only\n",
    "            with open('/dev/shm/micropython.py', 'w') as fout:\n",
    "                fout.write(cell)\n",
    "            proc = subprocess.Popen([\"../micropython/ports/unix/build-2/micropython-2\", \"/dev/shm/micropython.py\"], \n",
    "                                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n",
    "            print(proc.stdout.read().decode(\"utf-8\"))\n",
    "            print(proc.stderr.read().decode(\"utf-8\"))\n",
    "            return None\n",
    "        if args.file: # can be used to copy the cell content onto the pyboard's flash\n",
    "            spaces = \"    \"\n",
    "            try:\n",
    "                with open(args.file, 'w') as fout:\n",
    "                    fout.write(cell.replace('\\t', spaces))\n",
    "                    printf('written cell to {}'.format(args.file))\n",
    "            except:\n",
    "                print('Failed to write to disc!')\n",
    "            return None # do not parse the rest\n",
    "        if args.data: # can be used to load data from the pyboard directly into kernel space\n",
    "            message = pyb.exec(cell)\n",
    "            if len(message) == 0:\n",
    "                print('pyboard >>>')\n",
    "            else:\n",
    "                print(message.decode('utf-8'))\n",
    "                # register new variable in user namespace\n",
    "                self.shell.user_ns[args.data] = string_to_matrix(message.decode(\"utf-8\"))\n",
    "        \n",
    "        if args.time: # measures the time of executions\n",
    "            pyb.exec('import utime')\n",
    "            message = pyb.exec('t = utime.ticks_us()\\n' + cell + '\\ndelta = utime.ticks_diff(utime.ticks_us(), t)' + \n",
    "                               \"\\nprint('execution time: {:d} us'.format(delta))\")\n",
    "            print(message.decode('utf-8'))\n",
    "        \n",
    "        if args.memory: # prints out memory information \n",
    "            message = pyb.exec('from micropython import mem_info\\nprint(mem_info())\\n')\n",
    "            print(\"memory before execution:\\n========================\\n\", message.decode('utf-8'))\n",
    "            message = pyb.exec(cell)\n",
    "            print(\">>> \", message.decode('utf-8'))\n",
    "            message = pyb.exec('print(mem_info())')\n",
    "            print(\"memory after execution:\\n========================\\n\", message.decode('utf-8'))\n",
    "\n",
    "        if args.pyboard:\n",
    "            message = pyb.exec(cell)\n",
    "            print(message.decode('utf-8'))\n",
    "\n",
    "ip = get_ipython()\n",
    "ip.register_magics(PyboardMagic)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## pyboard"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-05-07T07:35:35.126401Z",
     "start_time": "2020-05-07T07:35:35.105824Z"
    }
   },
   "outputs": [],
   "source": [
    "import pyboard\n",
    "pyb = pyboard.Pyboard('/dev/ttyACM0')\n",
    "pyb.enter_raw_repl()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-05-19T19:11:18.145548Z",
     "start_time": "2020-05-19T19:11:18.137468Z"
    }
   },
   "outputs": [],
   "source": [
    "pyb.exit_raw_repl()\n",
    "pyb.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2020-05-07T07:35:38.725924Z",
     "start_time": "2020-05-07T07:35:38.645488Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -pyboard 1\n",
    "\n",
    "import utime\n",
    "import ulab as np\n",
    "\n",
    "def timeit(n=1000):\n",
    "    def wrapper(f, *args, **kwargs):\n",
    "        func_name = str(f).split(' ')[1]\n",
    "        def new_func(*args, **kwargs):\n",
    "            run_times = np.zeros(n, dtype=np.uint16)\n",
    "            for i in range(n):\n",
    "                t = utime.ticks_us()\n",
    "                result = f(*args, **kwargs)\n",
    "                run_times[i] = utime.ticks_diff(utime.ticks_us(), t)\n",
    "            print('{}() execution times based on {} cycles'.format(func_name, n, (delta2-delta1)/n))\n",
    "            print('\\tbest: %d us'%np.min(run_times))\n",
    "            print('\\tworst: %d us'%np.max(run_times))\n",
    "            print('\\taverage: %d us'%np.mean(run_times))\n",
    "            print('\\tdeviation: +/-%.3f us'%np.std(run_times))            \n",
    "            return result\n",
    "        return new_func\n",
    "    return wrapper\n",
    "\n",
    "def timeit(f, *args, **kwargs):\n",
    "    func_name = str(f).split(' ')[1]\n",
    "    def new_func(*args, **kwargs):\n",
    "        t = utime.ticks_us()\n",
    "        result = f(*args, **kwargs)\n",
    "        print('execution time: ', utime.ticks_diff(utime.ticks_us(), t), ' us')\n",
    "        return result\n",
    "    return new_func"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "__END_OF_DEFS__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Numpy functions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This section of the manual discusses those functions that were adapted from `numpy`. Starred functions accept complex arrays as arguments, if the firmware was compiled with complex support.\n",
    "\n",
    "1. [numpy.all*](#all)\n",
    "1. [numpy.any*](#any)\n",
    "1. [numpy.argmax](#argmax)\n",
    "1. [numpy.argmin](#argmin)\n",
    "1. [numpy.argsort](#argsort)\n",
    "1. [numpy.asarray*](#asarray)\n",
    "1. [numpy.bitwise_and](#bitwise_and)\n",
    "1. [numpy.bitwise_or](#bitwise_and)\n",
    "1. [numpy.bitwise_xor](#bitwise_and)\n",
    "1. [numpy.clip](#clip)\n",
    "1. [numpy.compress*](#compress)\n",
    "1. [numpy.conjugate*](#conjugate)\n",
    "1. [numpy.convolve*](#convolve)\n",
    "1. [numpy.delete](#delete)\n",
    "1. [numpy.diff](#diff)\n",
    "1. [numpy.dot](#dot)\n",
    "1. [numpy.equal](#equal)\n",
    "1. [numpy.flip*](#flip)\n",
    "1. [numpy.imag*](#imag)\n",
    "1. [numpy.interp](#interp)\n",
    "1. [numpy.isfinite](#isfinite)\n",
    "1. [numpy.isinf](#isinf)\n",
    "1. [numpy.left_shift](#left_shift)\n",
    "1. [numpy.load](#load)\n",
    "1. [numpy.loadtxt](#loadtxt)\n",
    "1. [numpy.max](#max)\n",
    "1. [numpy.maximum](#maximum)\n",
    "1. [numpy.mean](#mean)\n",
    "1. [numpy.median](#median)\n",
    "1. [numpy.min](#min)\n",
    "1. [numpy.minimum](#minimum)\n",
    "1. [numpy.nozero](#nonzero)\n",
    "1. [numpy.not_equal](#equal)\n",
    "1. [numpy.polyfit](#polyfit)\n",
    "1. [numpy.polyval](#polyval)\n",
    "1. [numpy.real*](#real)\n",
    "1. [numpy.right_shift](#right_shift)\n",
    "1. [numpy.roll](#roll)\n",
    "1. [numpy.save](#save)\n",
    "1. [numpy.savetxt](#savetxt)\n",
    "1. [numpy.size](#size)\n",
    "1. [numpy.sort](#sort)\n",
    "1. [numpy.sort_complex*](#sort_complex)\n",
    "1. [numpy.std](#std)\n",
    "1. [numpy.sum](#sum)\n",
    "1. [numpy.trace](#trace)\n",
    "1. [numpy.trapz](#trapz)\n",
    "1. [numpy.where](#where)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## all\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.all.html\n",
    "\n",
    "The function takes one positional, and one keyword argument, the `axis`, with a default value of `None`, and tests, whether *all* array elements along the given axis evaluate to `True`. If the keyword argument is `None`, the flattened array is inspected. \n",
    "\n",
    "Elements of an array evaluate to `True`, if they are not equal to zero, or the Boolean `False`. The return value if a Boolean `ndarray`.\n",
    "\n",
    "If the firmware was compiled with complex support, the function can accept complex arrays."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-02-08T16:54:57.117630Z",
     "start_time": "2021-02-08T16:54:57.105337Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "a:\n",
      " array([[0.0, 1.0, 2.0, 3.0],\n",
      "       [4.0, 5.0, 6.0, 7.0],\n",
      "       [8.0, 9.0, 10.0, 11.0]], dtype=float64)\n",
      "\n",
      "all of the flattened array:\n",
      " False\n",
      "\n",
      "all of a along 0th axis:\n",
      " array([False, True, True, True], dtype=bool)\n",
      "\n",
      "all of a along 1st axis:\n",
      " array([False, True, True], dtype=bool)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array(range(12)).reshape((3, 4))\n",
    "\n",
    "print('\\na:\\n', a)\n",
    "\n",
    "b = np.all(a)\n",
    "print('\\nall of the flattened array:\\n', b)\n",
    "\n",
    "c = np.all(a, axis=0)\n",
    "print('\\nall of a along 0th axis:\\n', c)\n",
    "\n",
    "d = np.all(a, axis=1)\n",
    "print('\\nall of a along 1st axis:\\n', d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## any\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.any.html\n",
    "\n",
    "The function takes one positional, and one keyword argument, the `axis`, with a default value of `None`, and tests, whether *any* array element along the given axis evaluates to `True`. If the keyword argument is `None`, the flattened array is inspected. \n",
    "\n",
    "Elements of an array evaluate to `True`, if they are not equal to zero, or the Boolean `False`. The return value if a Boolean `ndarray`.\n",
    "\n",
    "If the firmware was compiled with complex support, the function can accept complex arrays."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-02-08T16:54:14.704132Z",
     "start_time": "2021-02-08T16:54:14.693700Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "a:\n",
      " array([[0.0, 1.0, 2.0, 3.0],\n",
      "       [4.0, 5.0, 6.0, 7.0],\n",
      "       [8.0, 9.0, 10.0, 11.0]], dtype=float64)\n",
      "\n",
      "any of the flattened array:\n",
      " True\n",
      "\n",
      "any of a along 0th axis:\n",
      " array([True, True, True, True], dtype=bool)\n",
      "\n",
      "any of a along 1st axis:\n",
      " array([True, True, True], dtype=bool)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array(range(12)).reshape((3, 4))\n",
    "\n",
    "print('\\na:\\n', a)\n",
    "\n",
    "b = np.any(a)\n",
    "print('\\nany of the flattened array:\\n', b)\n",
    "\n",
    "c = np.any(a, axis=0)\n",
    "print('\\nany of a along 0th axis:\\n', c)\n",
    "\n",
    "d = np.any(a, axis=1)\n",
    "print('\\nany of a along 1st axis:\\n', d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## argmax\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.argmax.html\n",
    "\n",
    "See [numpy.max](#max)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## argmin\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.argmin.html\n",
    "\n",
    "See [numpy.max](#max)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## argsort\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.argsort.html\n",
    "\n",
    "Similarly to [sort](#sort), `argsort` takes a positional, and a keyword argument, and returns an unsigned short index array of type `ndarray` with the same dimensions as the input, or, if `axis=None`, as a row vector with length equal to the number of elements in the input (i.e., the flattened array). The indices in the output sort the input in ascending order. The routine in `argsort` is the same as in `sort`, therefore, the comments on computational expenses (time and RAM) also apply. In particular, since no copy of the original data is required, virtually no RAM beyond the output array is used. \n",
    "\n",
    "Since the underlying container of the output array is of type `uint16_t`, neither of the output dimensions should be larger than 65535. If that happens to be the case, the function will bail out with a `ValueError`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T16:33:33.292717Z",
     "start_time": "2021-01-13T16:33:33.280144Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "a:\n",
      " array([[1.0, 12.0, 3.0, 0.0],\n",
      "       [5.0, 3.0, 4.0, 1.0],\n",
      "       [9.0, 11.0, 1.0, 8.0],\n",
      "       [7.0, 10.0, 0.0, 1.0]], dtype=float64)\n",
      "\n",
      "a sorted along vertical axis:\n",
      " array([[0, 1, 3, 0],\n",
      "       [1, 3, 2, 1],\n",
      "       [3, 2, 0, 3],\n",
      "       [2, 0, 1, 2]], dtype=uint16)\n",
      "\n",
      "a sorted along horizontal axis:\n",
      " array([[3, 0, 2, 1],\n",
      "       [3, 1, 2, 0],\n",
      "       [2, 3, 0, 1],\n",
      "       [2, 3, 0, 1]], dtype=uint16)\n",
      "\n",
      "Traceback (most recent call last):\n",
      "  File \"/dev/shm/micropython.py\", line 12, in <module>\n",
      "NotImplementedError: argsort is not implemented for flattened arrays\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([[1, 12, 3, 0], [5, 3, 4, 1], [9, 11, 1, 8], [7, 10, 0, 1]], dtype=np.float)\n",
    "print('\\na:\\n', a)\n",
    "b = np.argsort(a, axis=0)\n",
    "print('\\na sorted along vertical axis:\\n', b)\n",
    "\n",
    "c = np.argsort(a, axis=1)\n",
    "print('\\na sorted along horizontal axis:\\n', c)\n",
    "\n",
    "c = np.argsort(a, axis=None)\n",
    "print('\\nflattened a sorted:\\n', c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Since during the sorting, only the indices are shuffled, `argsort` does not modify the input array, as one can verify this by the following example:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T16:34:48.446211Z",
     "start_time": "2021-01-13T16:34:48.424276Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "a:\n",
      " array([0, 5, 1, 3, 2, 4], dtype=uint8)\n",
      "\n",
      "sorting indices:\n",
      " array([0, 2, 4, 3, 5, 1], dtype=uint16)\n",
      "\n",
      "the original array:\n",
      " array([0, 5, 1, 3, 2, 4], dtype=uint8)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([0, 5, 1, 3, 2, 4], dtype=np.uint8)\n",
    "print('\\na:\\n', a)\n",
    "b = np.argsort(a, axis=0)\n",
    "print('\\nsorting indices:\\n', b)\n",
    "print('\\nthe original array:\\n', a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## asarray\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.asarray.html\n",
    "\n",
    "The function takes a single positional argument, and an optional keyword argument, `dtype`, with a default value of `None`. \n",
    "\n",
    "If the positional argument is an `ndarray`, and its `dtypes` is identical to the value of the keyword argument, or if the keyword argument is `None`, then the positional argument is simply returned. If the original `dtype`, and the value of the keyword argument are different, then a copy is returned, with appropriate `dtype` conversion. \n",
    "\n",
    "If the positional argument is an iterable, then the function is simply an alias for `array`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-01-14T20:05:22.017031Z",
     "start_time": "2022-01-14T20:05:22.002463Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:array([0, 1, 2, 3, 4, 5, 6, 7, 8], dtype=uint8)\n",
      "b:array([0, 1, 2, 3, 4, 5, 6, 7, 8], dtype=uint8)\n",
      "a == b: True\n",
      "\n",
      "c:array([0, 1, 2, 3, 4, 5, 6, 7, 8], dtype=int8)\n",
      "a == c: False\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array(range(9), dtype=np.uint8)\n",
    "b = np.asarray(a)\n",
    "c = np.asarray(a, dtype=np.int8)\n",
    "print('a:{}'.format(a))\n",
    "print('b:{}'.format(b))\n",
    "print('a == b: {}'.format(a is b))\n",
    "\n",
    "print('\\nc:{}'.format(c))\n",
    "print('a == c: {}'.format(a is c))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## bitwise_and\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/routines.bitwise.html\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.bitwise_and.html\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.bitwise_or.html\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.bitwise_xor.html\n",
    "\n",
    "Each of `bitwise_and`, `bitwise_or`, and `bitwise_xor` takes two integer-type `ndarray`s as arguments, and returns the element-wise results of the `AND`, `OR`, and `XOR` operators. Broadcasting is supported. If the `dtype` of the input arrays is not an integer, and exception will be raised."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "array([0, 1, 2, 3, 4, 5, 6, 7], dtype=uint8)\n",
      "array([1, 2, 3, 4, 5, 6, 7, 8], dtype=uint8)\n",
      "\n",
      "bitwise_and:\n",
      " array([0, 0, 2, 0, 4, 4, 6, 0], dtype=uint8)\n",
      "\n",
      "bitwise_or:\n",
      " array([1, 3, 3, 7, 5, 7, 7, 15], dtype=uint8)\n",
      "\n",
      "bitwise_xor:\n",
      " array([1, 3, 1, 7, 1, 3, 1, 15], dtype=uint8)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array(range(8), dtype=np.uint8)\n",
    "b = a + 1\n",
    "\n",
    "print(a)\n",
    "print(b)\n",
    "print('\\nbitwise_and:\\n', np.bitwise_and(a, b))\n",
    "print('\\nbitwise_or:\\n', np.bitwise_or(a, b))\n",
    "print('\\nbitwise_xor:\\n', np.bitwise_xor(a, b))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## clip\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.clip.html\n",
    "\n",
    "Clips an array, i.e., values that are outside of an interval are clipped to the interval edges. The function is equivalent to `maximum(a_min, minimum(a, a_max))` broadcasting takes place exactly as in [minimum](#minimum). If the arrays are of different `dtype`, the output is upcast as in [Binary operators](#Binary-operators)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-08T13:22:14.147310Z",
     "start_time": "2021-01-08T13:22:14.123961Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:\t\t array([0, 1, 2, 3, 4, 5, 6, 7, 8], dtype=uint8)\n",
      "clipped:\t array([3, 3, 3, 3, 4, 5, 6, 7, 7], dtype=uint8)\n",
      "\n",
      "a:\t\t array([0, 1, 2, 3, 4, 5, 6, 7, 8], dtype=uint8)\n",
      "b:\t\t array([3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0, 3.0], dtype=float64)\n",
      "clipped:\t array([3.0, 3.0, 3.0, 3.0, 4.0, 5.0, 6.0, 7.0, 7.0], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array(range(9), dtype=np.uint8)\n",
    "print('a:\\t\\t', a)\n",
    "print('clipped:\\t', np.clip(a, 3, 7))\n",
    "\n",
    "b = 3 * np.ones(len(a), dtype=np.float)\n",
    "print('\\na:\\t\\t', a)\n",
    "print('b:\\t\\t', b)\n",
    "print('clipped:\\t', np.clip(a, b, 7))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## compress\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.compress.html\n",
    "\n",
    "The function returns selected slices of an array along given axis. If the axis keyword is `None`, the flattened array is used.\n",
    "\n",
    "If the firmware was compiled with complex support, the function can accept complex arguments."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-01-07T19:51:44.994323Z",
     "start_time": "2022-01-07T19:51:44.978185Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:\n",
      " array([[0.0, 1.0, 2.0],\n",
      "       [3.0, 4.0, 5.0]], dtype=float64)\n",
      "\n",
      "compress(a):\n",
      " array([[3.0, 4.0, 5.0]], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array(range(6)).reshape((2, 3))\n",
    "\n",
    "print('a:\\n', a)\n",
    "print('\\ncompress(a):\\n', np.compress([0, 1], a, axis=0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## conjugate\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.conjugate.html\n",
    "\n",
    "If the firmware was compiled with complex support, the function calculates the complex conjugate of the input array. If the input array is of real `dtype`, then the output is simply a copy, preserving the `dtype`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-01-07T19:30:53.394539Z",
     "start_time": "2022-01-07T19:30:53.374737Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:\t\t array([1, 2, 3, 4], dtype=uint8)\n",
      "conjugate(a):\t array([1, 2, 3, 4], dtype=uint8)\n",
      "\n",
      "b:\t\t array([1.0+1.0j, 2.0-2.0j, 3.0+3.0j, 4.0-4.0j], dtype=complex)\n",
      "conjugate(b):\t array([1.0-1.0j, 2.0+2.0j, 3.0-3.0j, 4.0+4.0j], dtype=complex)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([1, 2, 3, 4], dtype=np.uint8)\n",
    "b = np.array([1+1j, 2-2j, 3+3j, 4-4j], dtype=np.complex)\n",
    "\n",
    "print('a:\\t\\t', a)\n",
    "print('conjugate(a):\\t', np.conjugate(a))\n",
    "print()\n",
    "print('b:\\t\\t', b)\n",
    "print('conjugate(b):\\t', np.conjugate(b))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## convolve\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.convolve.html\n",
    "\n",
    "Returns the discrete, linear convolution of two one-dimensional arrays.\n",
    "\n",
    "Only the ``full`` mode is supported, and the ``mode`` named parameter is not accepted. Note that all other modes can be had by slicing a ``full`` result.\n",
    "\n",
    "If the firmware was compiled with complex support, the function can accept complex arrays."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T15:57:39.028884Z",
     "start_time": "2021-01-13T15:57:39.008749Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "array([1.0, 12.0, 123.0, 1230.0, 2300.0, 3000.0], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "x = np.array((1, 2, 3))\n",
    "y = np.array((1, 10, 100, 1000))\n",
    "\n",
    "print(np.convolve(x, y))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## delete\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.delete.html\n",
    "\n",
    "The function returns a new array with sub-arrays along an axis deleted. It takes two positional arguments, the array, and the indices, which will be removed, as well as the `axis` keyword argument with a default value of `None`. If the `axis` is `None`, the will be flattened first. \n",
    "\n",
    "The second positional argument can be a scalar, or any `micropython` iterable. Since `range` can also be passed in place of the indices, slicing can be emulated. If the indices are negative, the elements are counted from the end of the axis.\n",
    "\n",
    "Note that the function creates a copy of the indices first, because it is not guaranteed that the indices are ordered. Keep this in mind, when working with large arrays."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-01-12T17:03:29.099233Z",
     "start_time": "2022-01-12T17:03:29.084117Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:\n",
      " array([[0, 1, 2, 3, 4],\n",
      "       [5, 6, 7, 8, 9],\n",
      "       [10, 11, 12, 13, 14],\n",
      "       [15, 16, 17, 18, 19],\n",
      "       [20, 21, 22, 23, 24]], dtype=uint8)\n",
      "\n",
      "axis = 0\n",
      " array([[0, 1, 2, 3, 4],\n",
      "       [5, 6, 7, 8, 9],\n",
      "       [15, 16, 17, 18, 19],\n",
      "       [20, 21, 22, 23, 24]], dtype=uint8)\n",
      "\n",
      "axis = 1\n",
      " array([[0, 1, 2, 4],\n",
      "       [5, 6, 7, 9],\n",
      "       [10, 11, 12, 14],\n",
      "       [15, 16, 17, 19],\n",
      "       [20, 21, 22, 24]], dtype=uint8)\n",
      "\n",
      "axis = None\n",
      " array([3, 4, 5, ..., 21, 23, 24], dtype=uint8)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array(range(25), dtype=np.uint8).reshape((5,5))\n",
    "print('a:\\n', a)\n",
    "print('\\naxis = 0\\n', np.delete(a, 2, axis=0))\n",
    "print('\\naxis = 1\\n', np.delete(a, -2, axis=1))\n",
    "print('\\naxis = None\\n', np.delete(a, [0, 1, 2, 22]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## diff\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.diff.html\n",
    "\n",
    "The `diff` function returns the numerical derivative of the forward scheme, or more accurately, the differences of an `ndarray` along a given axis. The order of derivative can be stipulated with the `n` keyword argument, which should be between 0, and 9. Default is 1. If higher order derivatives are required, they can be gotten by repeated calls to the function. The `axis` keyword argument should  be -1 (last axis, in `ulab` equivalent to the second axis, and this also happens to be the default value), 0, or 1. \n",
    "\n",
    "Beyond the output array, the function requires only a couple of bytes of extra RAM for the differentiation stencil. (The stencil is an `int8` array, one byte longer than `n`. This also explains, why the highest order is 9: the coefficients of a ninth-order stencil all fit in signed bytes, while 10 would require `int16`.) Note that as usual in numerical differentiation (and also in `numpy`), the length of the respective axis will be reduced by `n` after the operation. If `n` is larger than, or equal to the length of the axis, an empty array will be returned.\n",
    "\n",
    "**WARNING**: the `diff` function does not implement the `prepend` and `append` keywords that can be found in `numpy`. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 106,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-14T16:06:27.468909Z",
     "start_time": "2021-01-14T16:06:27.439067Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:\n",
      " array([0, 1, 2, 10, 4, 5, 6, 7, 8], dtype=uint8)\n",
      "\n",
      "first derivative:\n",
      " array([1, 1, 8, 250, 1, 1, 1, 1], dtype=uint8)\n",
      "\n",
      "second derivative:\n",
      " array([0, 249, 14, 249, 0, 0, 0], dtype=uint8)\n",
      "\n",
      "c:\n",
      " array([[1.0, 2.0, 3.0, 4.0],\n",
      "       [4.0, 3.0, 2.0, 1.0],\n",
      "       [1.0, 4.0, 9.0, 16.0],\n",
      "       [0.0, 0.0, 0.0, 0.0]], dtype=float64)\n",
      "\n",
      "first derivative, first axis:\n",
      " array([[3.0, 1.0, -1.0, -3.0],\n",
      "       [-3.0, 1.0, 7.0, 15.0],\n",
      "       [-1.0, -4.0, -9.0, -16.0]], dtype=float64)\n",
      "\n",
      "first derivative, second axis:\n",
      " array([[1.0, 1.0, 1.0],\n",
      "       [-1.0, -1.0, -1.0],\n",
      "       [3.0, 5.0, 7.0],\n",
      "       [0.0, 0.0, 0.0]], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array(range(9), dtype=np.uint8)\n",
    "a[3] = 10\n",
    "print('a:\\n', a)\n",
    "\n",
    "print('\\nfirst derivative:\\n', np.diff(a, n=1))\n",
    "print('\\nsecond derivative:\\n', np.diff(a, n=2))\n",
    "\n",
    "c = np.array([[1, 2, 3, 4], [4, 3, 2, 1], [1, 4, 9, 16], [0, 0, 0, 0]])\n",
    "print('\\nc:\\n', c)\n",
    "print('\\nfirst derivative, first axis:\\n', np.diff(c, axis=0))\n",
    "print('\\nfirst derivative, second axis:\\n', np.diff(c, axis=1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## dot\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.dot.html\n",
    "\n",
    "\n",
    "**WARNING:** numpy applies upcasting rules for the multiplication of matrices, while `ulab` simply returns a float matrix. \n",
    "\n",
    "Once you can invert a matrix, you might want to know, whether the inversion is correct. You can simply take the original matrix and its inverse, and multiply them by calling the `dot` function, which takes the two matrices as its arguments. If the matrix dimensions do not match, the function raises a `ValueError`. The result of the multiplication is expected to be the unit matrix, which is demonstrated below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-02-13T08:32:09.139378Z",
     "start_time": "2021-02-13T08:32:09.122083Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "m:\n",
      " array([[1, 2, 3],\n",
      "       [4, 5, 6],\n",
      "       [7, 10, 9]], dtype=uint8)\n",
      "\n",
      "m^-1:\n",
      " array([[-1.25, 1.0, -0.25],\n",
      "       [0.4999999999999998, -1.0, 0.5],\n",
      "       [0.4166666666666668, 0.3333333333333333, -0.25]], dtype=float64)\n",
      "\n",
      "m*m^-1:\n",
      " array([[1.0, 0.0, 0.0],\n",
      "       [4.440892098500626e-16, 1.0, 0.0],\n",
      "       [8.881784197001252e-16, 0.0, 1.0]], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "m = np.array([[1, 2, 3], [4, 5, 6], [7, 10, 9]], dtype=np.uint8)\n",
    "n = np.linalg.inv(m)\n",
    "print(\"m:\\n\", m)\n",
    "print(\"\\nm^-1:\\n\", n)\n",
    "# this should be the unit matrix\n",
    "print(\"\\nm*m^-1:\\n\", np.dot(m, n))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Note that for matrix multiplication you don't necessarily need square matrices, it is enough, if their dimensions are compatible (i.e., the the left-hand-side matrix has as many columns, as does the right-hand-side matrix rows):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-02-13T08:33:07.630825Z",
     "start_time": "2021-02-13T08:33:07.608260Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "array([[1, 2, 3, 4],\n",
      "       [5, 6, 7, 8]], dtype=uint8)\n",
      "array([[1, 2],\n",
      "       [3, 4],\n",
      "       [5, 6],\n",
      "       [7, 8]], dtype=uint8)\n",
      "array([[50.0, 60.0],\n",
      "       [114.0, 140.0]], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "m = np.array([[1, 2, 3, 4], [5, 6, 7, 8]], dtype=np.uint8)\n",
    "n = np.array([[1, 2], [3, 4], [5, 6], [7, 8]], dtype=np.uint8)\n",
    "print(m)\n",
    "print(n)\n",
    "print(np.dot(m, n))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## equal\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.equal.html\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.not_equal.html\n",
    "\n",
    "In `micropython`, equality of arrays or scalars can be established by utilising the `==`, `!=`, `<`, `>`, `<=`, or `=>` binary operators. In `circuitpython`, `==` and `!=` will produce unexpected results. In order to avoid this discrepancy, and to maintain compatibility with `numpy`, `ulab` implements the `equal` and `not_equal` operators that return the same results, irrespective of the `python` implementation.\n",
    "\n",
    "These two functions take two `ndarray`s, or scalars as their arguments. No keyword arguments are implemented."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-08T14:22:13.990898Z",
     "start_time": "2021-01-08T14:22:13.941896Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:  array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], dtype=float64)\n",
      "b:  array([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], dtype=float64)\n",
      "\n",
      "a == b:  array([True, False, False, False, False, False, False, False, False], dtype=bool)\n",
      "a != b:  array([False, True, True, True, True, True, True, True, True], dtype=bool)\n",
      "a == 2:  array([False, False, True, False, False, False, False, False, False], dtype=bool)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array(range(9))\n",
    "b = np.zeros(9)\n",
    "\n",
    "print('a: ', a)\n",
    "print('b: ', b)\n",
    "print('\\na == b: ', np.equal(a, b))\n",
    "print('a != b: ', np.not_equal(a, b))\n",
    "\n",
    "# comparison with scalars\n",
    "print('a == 2: ', np.equal(a, 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## flip\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.flip.html\n",
    "\n",
    "The `flip` function takes one positional, an `ndarray`, and one keyword argument, `axis = None`, and reverses the order of elements along the given axis. If the keyword argument is `None`, the matrix' entries are flipped along all axes. `flip` returns a new copy of the array.\n",
    "\n",
    "If the firmware was compiled with complex support, the function can accept complex arrays."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T16:25:08.425583Z",
     "start_time": "2021-01-13T16:25:08.407004Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a: \t array([1.0, 2.0, 3.0, 4.0, 5.0], dtype=float64)\n",
      "a flipped:\t array([5.0, 4.0, 3.0, 2.0, 1.0], dtype=float64)\n",
      "\n",
      "a flipped horizontally\n",
      " array([[3, 2, 1],\n",
      "       [6, 5, 4],\n",
      "       [9, 8, 7]], dtype=uint8)\n",
      "\n",
      "a flipped vertically\n",
      " array([[7, 8, 9],\n",
      "       [4, 5, 6],\n",
      "       [1, 2, 3]], dtype=uint8)\n",
      "\n",
      "a flipped horizontally+vertically\n",
      " array([9, 8, 7, 6, 5, 4, 3, 2, 1], dtype=uint8)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([1, 2, 3, 4, 5])\n",
    "print(\"a: \\t\", a)\n",
    "print(\"a flipped:\\t\", np.flip(a))\n",
    "\n",
    "a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.uint8)\n",
    "print(\"\\na flipped horizontally\\n\", np.flip(a, axis=1))\n",
    "print(\"\\na flipped vertically\\n\", np.flip(a, axis=0))\n",
    "print(\"\\na flipped horizontally+vertically\\n\", np.flip(a))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## imag\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.imag.html\n",
    "\n",
    "The `imag` function returns the imaginary part of an array, or scalar. It cannot accept a generic iterable as its argument. The function is defined only, if the firmware was compiled with complex support."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-01-07T19:26:42.901258Z",
     "start_time": "2022-01-07T19:26:42.880755Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:\t\t array([1, 2, 3], dtype=uint16)\n",
      "imag(a):\t array([0, 0, 0], dtype=uint16)\n",
      "\n",
      "b:\t\t array([1.0+0.0j, 2.0+1.0j, 3.0-1.0j], dtype=complex)\n",
      "imag(b):\t array([0.0, 1.0, -1.0], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([1, 2, 3], dtype=np.uint16)\n",
    "print(\"a:\\t\\t\", a)\n",
    "print(\"imag(a):\\t\", np.imag(a))\n",
    "\n",
    "b = np.array([1, 2+1j, 3-1j], dtype=np.complex)\n",
    "print(\"\\nb:\\t\\t\", b)\n",
    "print(\"imag(b):\\t\", np.imag(b))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## interp\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/numpy.interp\n",
    "\n",
    "The `interp` function returns the linearly interpolated values of a one-dimensional numerical array. It requires three positional arguments,`x`, at which the interpolated values are evaluated, `xp`, the array\n",
    "of the independent data variable, and `fp`, the array of the dependent values of the data. `xp` must be a monotonically increasing sequence of numbers.\n",
    "\n",
    "Two keyword arguments, `left`, and `right` can also be supplied; these determine the return values, if `x < xp[0]`, and `x > xp[-1]`, respectively. If these arguments are not supplied, `left`, and `right` default to `fp[0]`, and `fp[-1]`, respectively."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T16:00:43.505722Z",
     "start_time": "2021-01-13T16:00:43.489060Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "array([0.8, 1.8, 2.8, 3.8, 4.8], dtype=float64)\n",
      "array([1.0, 1.8, 2.8, 4.6, 5.0], dtype=float64)\n",
      "array([0.0, 1.8, 2.8, 4.6, 5.0], dtype=float64)\n",
      "array([1.0, 1.8, 2.8, 4.6, 10.0], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "x = np.array([1, 2, 3, 4, 5]) - 0.2\n",
    "xp = np.array([1, 2, 3, 4])\n",
    "fp = np.array([1, 2, 3, 5])\n",
    "\n",
    "print(x)\n",
    "print(np.interp(x, xp, fp))\n",
    "print(np.interp(x, xp, fp, left=0.0))\n",
    "print(np.interp(x, xp, fp, right=10.0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## isfinite\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.isfinite.html\n",
    "\n",
    "Returns a Boolean array of the same shape as the input, or a `True/False`, if the input is a scalar.  In the return value, all elements are `True` at positions,  where the input value was finite. Integer types are automatically finite, therefore, if the input is of integer type, the output will be the `True` tensor."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-29T21:34:42.026689Z",
     "start_time": "2021-01-29T21:34:42.010935Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "isfinite(0):  True\n",
      "\n",
      "====================\n",
      "a:\n",
      " array([1.0, 2.0, nan], dtype=float64)\n",
      "\n",
      "isfinite(a):\n",
      " array([True, True, False], dtype=bool)\n",
      "\n",
      "====================\n",
      "b:\n",
      " array([1.0, 2.0, inf], dtype=float64)\n",
      "\n",
      "isfinite(b):\n",
      " array([True, True, False], dtype=bool)\n",
      "\n",
      "====================\n",
      "c:\n",
      " array([1, 2, 3], dtype=uint16)\n",
      "\n",
      "isfinite(c):\n",
      " array([True, True, True], dtype=bool)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "print('isfinite(0): ', np.isfinite(0))\n",
    "\n",
    "a = np.array([1, 2, np.nan])\n",
    "print('\\n' + '='*20)\n",
    "print('a:\\n', a)\n",
    "print('\\nisfinite(a):\\n', np.isfinite(a))\n",
    "\n",
    "b = np.array([1, 2, np.inf])\n",
    "print('\\n' + '='*20)\n",
    "print('b:\\n', b)\n",
    "print('\\nisfinite(b):\\n', np.isfinite(b))\n",
    "\n",
    "c = np.array([1, 2, 3], dtype=np.uint16)\n",
    "print('\\n' + '='*20)\n",
    "print('c:\\n', c)\n",
    "print('\\nisfinite(c):\\n', np.isfinite(c))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## isinf\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.isinf.html\n",
    "\n",
    "Similar to [isfinite](#isfinite), but the output is `True` at positions, where the input is infinite. Integer types return the `False` tensor."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-29T21:35:21.938514Z",
     "start_time": "2021-01-29T21:35:21.923741Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "isinf(0):  False\n",
      "\n",
      "====================\n",
      "a:\n",
      " array([1.0, 2.0, nan], dtype=float64)\n",
      "\n",
      "isinf(a):\n",
      " array([False, False, False], dtype=bool)\n",
      "\n",
      "====================\n",
      "b:\n",
      " array([1.0, 2.0, inf], dtype=float64)\n",
      "\n",
      "isinf(b):\n",
      " array([False, False, True], dtype=bool)\n",
      "\n",
      "====================\n",
      "c:\n",
      " array([1, 2, 3], dtype=uint16)\n",
      "\n",
      "isinf(c):\n",
      " array([False, False, False], dtype=bool)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "print('isinf(0): ', np.isinf(0))\n",
    "\n",
    "a = np.array([1, 2, np.nan])\n",
    "print('\\n' + '='*20)\n",
    "print('a:\\n', a)\n",
    "print('\\nisinf(a):\\n', np.isinf(a))\n",
    "\n",
    "b = np.array([1, 2, np.inf])\n",
    "print('\\n' + '='*20)\n",
    "print('b:\\n', b)\n",
    "print('\\nisinf(b):\\n', np.isinf(b))\n",
    "\n",
    "c = np.array([1, 2, 3], dtype=np.uint16)\n",
    "print('\\n' + '='*20)\n",
    "print('c:\\n', c)\n",
    "print('\\nisinf(c):\\n', np.isinf(c))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## left_shift\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.left_shift.html\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.right_shift.html\n",
    "\n",
    "`left_shift`, and `right_shift` both take two integer-type `ndarray`s, and bit-wise shift the elements of the first array by an amount given by the second array to the left, and right, respectively. Broadcasting is supported. If the `dtype` of the input arrays is not an integer, and exception will be raised."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:  array([1, 1, 1, 1, 1, 1, 1], dtype=uint8)\n",
      "b:  array([255, 255, 255, 255, 255, 255, 255], dtype=uint8)\n",
      "c:  array([1, 2, 3, 4, 5, 6, 7], dtype=uint8)\n",
      "\n",
      "a left shifted by c:\n",
      " array([2, 4, 8, 16, 32, 64, 128], dtype=uint8)\n",
      "\n",
      "b right shifted by c:\n",
      " array([127, 63, 31, 15, 7, 3, 1], dtype=uint8)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.ones(7, dtype=np.uint8)\n",
    "b = np.zeros(7, dtype=np.uint8) + 255\n",
    "c = np.array(range(7), dtype=np.uint8) + 1\n",
    "\n",
    "print('a: ', a)\n",
    "print('b: ', b)\n",
    "print('c: ', c)\n",
    "print('\\na left shifted by c:\\n', np.left_shift(a, c))\n",
    "print('\\nb right shifted by c:\\n', np.right_shift(b, c))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## load\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.load.html\n",
    "\n",
    "The function reads data from a file in `numpy`'s [platform-independent format](https://numpy.org/doc/stable/reference/generated/numpy.lib.format.html#module-numpy.lib.format), and returns the generated array. If the endianness of the data in the file and the microcontroller differ, the bytes are automatically swapped."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-01-12T19:11:10.361592Z",
     "start_time": "2022-01-12T19:11:10.342439Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "array([[0.0, 1.0, 2.0, 3.0, 4.0],\n",
      "       [5.0, 6.0, 7.0, 8.0, 9.0],\n",
      "       [10.0, 11.0, 12.0, 13.0, 14.0],\n",
      "       [15.0, 16.0, 17.0, 18.0, 19.0],\n",
      "       [20.0, 21.0, 22.0, 23.0, 24.0]], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.load('a.npy')\n",
    "print(a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## loadtxt\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.loadtxt.html\n",
    "\n",
    "The function reads data from a text file, and returns the generated array. It takes a file name as the single positional argument, and the following keyword arguments:\n",
    "\n",
    "1. `comments='#'`\n",
    "1. `dtype=float`\n",
    "1. `delimiter=','`\n",
    "1. `max_rows` (with a default of all rows) \n",
    "1. `skip_rows=0`\n",
    "1. `usecols` (with a default of all columns)\n",
    "\n",
    "If `dtype` is supplied and is not `float`, the data entries will be converted to the appropriate integer type by rounding the values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-02-01T17:41:22.384706Z",
     "start_time": "2022-02-01T17:41:22.362821Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "read all data\n",
      "array([[0.0, 1.0, 2.0, 3.0],\n",
      "       [4.0, 5.0, 6.0, 7.0],\n",
      "       [8.0, 9.0, 10.0, 11.0],\n",
      "       [12.0, 13.0, 14.0, 15.0],\n",
      "       [16.0, 17.0, 18.0, 19.0],\n",
      "       [20.0, 21.0, 22.0, 23.0],\n",
      "       [24.0, 25.0, 26.0, 27.0],\n",
      "       [28.00000000000001, 29.0, 30.0, 31.0],\n",
      "       [32.0, 33.0, 34.00000000000001, 35.0]], dtype=float64)\n",
      "\n",
      "read maximum 5 rows (first row is a comment line)\n",
      "array([[0.0, 1.0, 2.0, 3.0],\n",
      "       [4.0, 5.0, 6.0, 7.0],\n",
      "       [8.0, 9.0, 10.0, 11.0],\n",
      "       [12.0, 13.0, 14.0, 15.0]], dtype=float64)\n",
      "\n",
      "read maximum 5 rows, convert dtype (first row is a comment line)\n",
      "array([[0, 1, 2, 3],\n",
      "       [4, 5, 6, 7],\n",
      "       [8, 9, 10, 11],\n",
      "       [12, 13, 14, 15]], dtype=uint8)\n",
      "\n",
      "skip the first 3 rows, convert dtype (first row is a comment line)\n",
      "array([[8, 9, 10, 11],\n",
      "       [12, 13, 14, 15],\n",
      "       [16, 17, 18, 19],\n",
      "       [20, 21, 22, 23],\n",
      "       [24, 25, 26, 27],\n",
      "       [28, 29, 30, 31],\n",
      "       [32, 33, 34, 35]], dtype=uint8)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "print('read all data')\n",
    "print(np.loadtxt('loadtxt.dat'))\n",
    "\n",
    "print('\\nread maximum 5 rows (first row is a comment line)')\n",
    "print(np.loadtxt('loadtxt.dat', max_rows=5))\n",
    "\n",
    "print('\\nread maximum 5 rows, convert dtype (first row is a comment line)')\n",
    "print(np.loadtxt('loadtxt.dat', max_rows=5, dtype=np.uint8))\n",
    "\n",
    "print('\\nskip the first 3 rows, convert dtype (first row is a comment line)')\n",
    "print(np.loadtxt('loadtxt.dat', skiprows=3, dtype=np.uint8))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## mean\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.mean.html\n",
    "\n",
    "If the axis keyword is not specified, it assumes the default value of `None`, and returns the result of the computation for the flattened array. Otherwise, the calculation is along the given axis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T16:15:39.921212Z",
     "start_time": "2021-01-13T16:15:39.908217Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a: \n",
      " array([[1.0, 2.0, 3.0],\n",
      "       [4.0, 5.0, 6.0],\n",
      "       [7.0, 8.0, 9.0]], dtype=float64)\n",
      "mean, flat:  5.0\n",
      "mean, horizontal:  array([2.0, 5.0, 8.0], dtype=float64)\n",
      "mean, vertical:  array([4.0, 5.0, 6.0], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
    "print('a: \\n', a)\n",
    "print('mean, flat: ', np.mean(a))\n",
    "print('mean, horizontal: ', np.mean(a, axis=1))\n",
    "print('mean, vertical: ', np.mean(a, axis=0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## max\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.max.html\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.argmax.html\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.min.html\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.argmin.html\n",
    "\n",
    "**WARNING:** Difference to `numpy`: the `out` keyword argument is not implemented.\n",
    "\n",
    "These functions follow the same pattern, and work with generic iterables, and `ndarray`s. `min`, and `max` return the minimum or maximum of a sequence. If the input array is two-dimensional, the `axis` keyword argument can be supplied, in which case the minimum/maximum along the given axis will be returned. If `axis=None` (this is also the default value), the minimum/maximum of the flattened array will be determined.\n",
    "\n",
    "`argmin/argmax` return the position (index) of the minimum/maximum in the sequence."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T16:08:56.986619Z",
     "start_time": "2021-01-13T16:08:56.964492Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a: array([1.0, 2.0, 0.0, 1.0, 10.0], dtype=float64)\n",
      "min of a: 0.0\n",
      "argmin of a: 2\n",
      "\n",
      "b:\n",
      " array([[1.0, 2.0, 0.0],\n",
      "       [1.0, 10.0, -1.0]], dtype=float64)\n",
      "min of b (flattened): -1.0\n",
      "min of b (axis=0): array([1.0, 2.0, -1.0], dtype=float64)\n",
      "min of b (axis=1): array([0.0, -1.0], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([1, 2, 0, 1, 10])\n",
    "print('a:', a)\n",
    "print('min of a:', np.min(a))\n",
    "print('argmin of a:', np.argmin(a))\n",
    "\n",
    "b = np.array([[1, 2, 0], [1, 10, -1]])\n",
    "print('\\nb:\\n', b)\n",
    "print('min of b (flattened):', np.min(b))\n",
    "print('min of b (axis=0):', np.min(b, axis=0))\n",
    "print('min of b (axis=1):', np.min(b, axis=1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## median\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.median.html\n",
    "\n",
    "The function computes the median along the specified axis, and returns the median of the array elements. If the `axis` keyword argument is `None`, the arrays is flattened first. The `dtype` of the results is always float."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T16:31:13.833800Z",
     "start_time": "2021-01-13T16:31:13.809560Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:\n",
      " array([[0, 1, 2, 3],\n",
      "       [4, 5, 6, 7],\n",
      "       [8, 9, 10, 11]], dtype=int8)\n",
      "\n",
      "median of the flattened array:  5.5\n",
      "\n",
      "median along the vertical axis:  array([4.0, 5.0, 6.0, 7.0], dtype=float64)\n",
      "\n",
      "median along the horizontal axis:  array([1.5, 5.5, 9.5], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array(range(12), dtype=np.int8).reshape((3, 4))\n",
    "print('a:\\n', a)\n",
    "print('\\nmedian of the flattened array: ', np.median(a))\n",
    "print('\\nmedian along the vertical axis: ', np.median(a, axis=0))\n",
    "print('\\nmedian along the horizontal axis: ', np.median(a, axis=1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## min\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.min.html\n",
    "\n",
    "See [numpy.max](#max)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## minimum\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.minimum.html\n",
    "\n",
    "See [numpy.maximum](#maximum)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## maximum\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.maximum.html\n",
    "\n",
    "Returns the maximum of two arrays, or two scalars, or an array, and a scalar. If the arrays are of different `dtype`, the output is upcast as in [Binary operators](#Binary-operators). If both inputs are scalars, a scalar is returned. Only positional arguments are implemented."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-08T13:21:17.151280Z",
     "start_time": "2021-01-08T13:21:17.123768Z"
    },
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "minimum of a, and b:\n",
      "array([1.0, 2.0, 3.0, 2.0, 1.0], dtype=float64)\n",
      "\n",
      "maximum of a, and b:\n",
      "array([5.0, 4.0, 3.0, 4.0, 5.0], dtype=float64)\n",
      "\n",
      "maximum of 1, and 5.5:\n",
      "5.5\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([1, 2, 3, 4, 5], dtype=np.uint8)\n",
    "b = np.array([5, 4, 3, 2, 1], dtype=np.float)\n",
    "print('minimum of a, and b:')\n",
    "print(np.minimum(a, b))\n",
    "\n",
    "print('\\nmaximum of a, and b:')\n",
    "print(np.maximum(a, b))\n",
    "\n",
    "print('\\nmaximum of 1, and 5.5:')\n",
    "print(np.maximum(1, 5.5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## nonzero\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.nonzero.html\n",
    "\n",
    "`nonzero` returns the indices of the elements of an array that are not zero. If the number of dimensions of the array is larger than one, a tuple of arrays is returned, one for each dimension, containing the indices of the non-zero elements in that dimension."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:\n",
      " array([-5.0, -4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0], dtype=float64)\n",
      "(array([0, 1, 2, 3, 4, 6, 7, 8], dtype=uint16),)\n",
      "\n",
      "a:\n",
      " array([[-5.0, -4.0, -3.0],\n",
      "       [-2.0, -1.0, 0.0],\n",
      "       [1.0, 2.0, 3.0]], dtype=float64)\n",
      "(array([0, 0, 0, 1, 1, 2, 2, 2], dtype=uint16), array([0, 1, 2, 0, 1, 0, 1, 2], dtype=uint16))\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array(range(9)) - 5\n",
    "print('a:\\n', a)\n",
    "print(np.nonzero(a))\n",
    "\n",
    "a = a.reshape((3,3))\n",
    "print('\\na:\\n', a)\n",
    "print(np.nonzero(a))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## not_equal\n",
    "\n",
    "See [numpy.equal](#equal)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## polyfit\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.polyfit.html\n",
    "\n",
    "`polyfit` takes two, or three arguments. The last one is the degree of the polynomial that will be fitted, the last but one is an array or iterable with the `y` (dependent) values, and the first one, an array or iterable with the `x` (independent) values, can be dropped. If that is the case, `x` will be generated in the function as `range(len(y))`.\n",
    "\n",
    "If the lengths of `x`, and `y` are not the same, the function raises a `ValueError`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T18:23:39.238450Z",
     "start_time": "2021-01-13T18:23:39.221063Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "independent values:\t array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0], dtype=float64)\n",
      "dependent values:\t array([9.0, 4.0, 1.0, 0.0, 1.0, 4.0, 9.0], dtype=float64)\n",
      "fitted values:\t\t array([1.0, -6.0, 9.000000000000004], dtype=float64)\n",
      "\n",
      "dependent values:\t array([9.0, 4.0, 1.0, 0.0, 1.0, 4.0, 9.0], dtype=float64)\n",
      "fitted values:\t\t array([1.0, -6.0, 9.000000000000004], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "x = np.array([0, 1, 2, 3, 4, 5, 6])\n",
    "y = np.array([9, 4, 1, 0, 1, 4, 9])\n",
    "print('independent values:\\t', x)\n",
    "print('dependent values:\\t', y)\n",
    "print('fitted values:\\t\\t', np.polyfit(x, y, 2))\n",
    "\n",
    "# the same with missing x\n",
    "print('\\ndependent values:\\t', y)\n",
    "print('fitted values:\\t\\t', np.polyfit(y, 2))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Execution time\n",
    "\n",
    "`polyfit` is based on the inversion of a matrix (there is more on the background in  https://en.wikipedia.org/wiki/Polynomial_regression), and it requires the intermediate storage of `2*N*(deg+1)` floats, where `N` is the number of entries in the input array, and `deg` is the fit's degree. The additional computation costs of the matrix inversion discussed in [linalg.inv](#inv) also apply. The example from above needs around 150 microseconds to return:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T18:31:40.919764Z",
     "start_time": "2021-01-13T18:31:40.912817Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "execution time:  153  us\n"
     ]
    }
   ],
   "source": [
    "%%micropython -pyboard 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "@timeit\n",
    "def time_polyfit(x, y, n):\n",
    "    return np.polyfit(x, y, n)\n",
    "\n",
    "x = np.array([0, 1, 2, 3, 4, 5, 6])\n",
    "y = np.array([9, 4, 1, 0, 1, 4, 9])\n",
    "\n",
    "time_polyfit(x, y, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## polyval\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.polyval.html\n",
    "\n",
    "`polyval` takes two arguments, both arrays or generic `micropython` iterables returning scalars."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T18:12:56.736643Z",
     "start_time": "2021-01-13T18:12:56.668042Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "coefficients:  [1, 1, 1, 0]\n",
      "independent values:  [0, 1, 2, 3, 4]\n",
      "\n",
      "values of p(x):  array([0.0, 3.0, 14.0, 39.0, 84.0], dtype=float64)\n",
      "\n",
      "ndarray (a):  array([0.0, 1.0, 2.0, 3.0, 4.0], dtype=float64)\n",
      "value of p(a):  array([0.0, 3.0, 14.0, 39.0, 84.0], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "p = [1, 1, 1, 0]\n",
    "x = [0, 1, 2, 3, 4]\n",
    "print('coefficients: ', p)\n",
    "print('independent values: ', x)\n",
    "print('\\nvalues of p(x): ', np.polyval(p, x))\n",
    "\n",
    "# the same works with one-dimensional ndarrays\n",
    "a = np.array(x)\n",
    "print('\\nndarray (a): ', a)\n",
    "print('value of p(a): ', np.polyval(p, a))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## real\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.real.html\n",
    "\n",
    "The `real` function returns the real part of an array, or scalar. It cannot accept a generic iterable as its argument. The function is defined only, if the firmware was compiled with complex support."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-01-07T19:27:22.141930Z",
     "start_time": "2022-01-07T19:27:22.122577Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:\t\t array([1, 2, 3], dtype=uint16)\n",
      "real(a):\t array([1, 2, 3], dtype=uint16)\n",
      "\n",
      "b:\t\t array([1.0+0.0j, 2.0+1.0j, 3.0-1.0j], dtype=complex)\n",
      "real(b):\t array([1.0, 2.0, 3.0], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([1, 2, 3], dtype=np.uint16)\n",
    "print(\"a:\\t\\t\", a)\n",
    "print(\"real(a):\\t\", np.real(a))\n",
    "\n",
    "b = np.array([1, 2+1j, 3-1j], dtype=np.complex)\n",
    "print(\"\\nb:\\t\\t\", b)\n",
    "print(\"real(b):\\t\", np.real(b))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## roll\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.roll.html\n",
    "\n",
    "The roll function shifts the content of a vector by the positions given as the second argument. If the `axis` keyword is supplied, the shift is applied to the given axis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T16:18:30.387043Z",
     "start_time": "2021-01-13T16:18:30.363374Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:\t\t\t array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], dtype=float64)\n",
      "a rolled to the left:\t array([7.0, 8.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0], dtype=float64)\n",
      "a rolled to the right:\t array([1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([1, 2, 3, 4, 5, 6, 7, 8])\n",
    "print(\"a:\\t\\t\\t\", a)\n",
    "\n",
    "a = np.roll(a, 2)\n",
    "print(\"a rolled to the left:\\t\", a)\n",
    "\n",
    "# this should be the original vector\n",
    "a = np.roll(a, -2)\n",
    "print(\"a rolled to the right:\\t\", a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Rolling works with matrices, too. If the `axis` keyword is 0, the matrix is rolled along its vertical axis, otherwise, horizontally. \n",
    "\n",
    "Horizontal rolls are faster, because they require fewer steps, and larger memory chunks are copied, however, they also require more RAM: basically the whole row must be stored internally. Most expensive are the `None` keyword values, because with `axis = None`, the array is flattened first, hence the row's length is the size of the whole matrix.\n",
    "\n",
    "Vertical rolls require two internal copies of single columns. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T16:23:52.025977Z",
     "start_time": "2021-01-13T16:23:52.001252Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:\n",
      " array([[0.0, 1.0, 2.0, 3.0],\n",
      "       [4.0, 5.0, 6.0, 7.0],\n",
      "       [8.0, 9.0, 10.0, 11.0]], dtype=float64)\n",
      "\n",
      "a rolled up:\n",
      " array([[4.0, 5.0, 6.0, 7.0],\n",
      "       [8.0, 9.0, 10.0, 11.0],\n",
      "       [0.0, 1.0, 2.0, 3.0]], dtype=float64)\n",
      "a:\n",
      " array([[0.0, 1.0, 2.0, 3.0],\n",
      "       [4.0, 5.0, 6.0, 7.0],\n",
      "       [8.0, 9.0, 10.0, 11.0]], dtype=float64)\n",
      "\n",
      "a rolled to the left:\n",
      " array([[1.0, 2.0, 3.0, 0.0],\n",
      "       [5.0, 6.0, 7.0, 4.0],\n",
      "       [9.0, 10.0, 11.0, 8.0]], dtype=float64)\n",
      "a:\n",
      " array([[0.0, 1.0, 2.0, 3.0],\n",
      "       [4.0, 5.0, 6.0, 7.0],\n",
      "       [8.0, 9.0, 10.0, 11.0]], dtype=float64)\n",
      "\n",
      "a rolled with None:\n",
      " array([[11.0, 0.0, 1.0, 2.0],\n",
      "       [3.0, 4.0, 5.0, 6.0],\n",
      "       [7.0, 8.0, 9.0, 10.0]], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array(range(12)).reshape((3, 4))\n",
    "print(\"a:\\n\", a)\n",
    "a = np.roll(a, 2, axis=0)\n",
    "print(\"\\na rolled up:\\n\", a)\n",
    "\n",
    "a = np.array(range(12)).reshape((3, 4))\n",
    "print(\"a:\\n\", a)\n",
    "a = np.roll(a, -1, axis=1)\n",
    "print(\"\\na rolled to the left:\\n\", a)\n",
    "\n",
    "a = np.array(range(12)).reshape((3, 4))\n",
    "print(\"a:\\n\", a)\n",
    "a = np.roll(a, 1, axis=None)\n",
    "print(\"\\na rolled with None:\\n\", a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## save\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.save.html\n",
    "\n",
    "With the help of this function, numerical array can be saved in `numpy`'s [platform-independent format](https://numpy.org/doc/stable/reference/generated/numpy.lib.format.html#module-numpy.lib.format).\n",
    "\n",
    "The function takes two positional arguments, the name of the output file, and the array. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-01-15T08:51:08.827144Z",
     "start_time": "2022-01-15T08:51:08.813813Z"
    }
   },
   "outputs": [],
   "source": [
    "a = np.array(range(25)).reshape((5, 5))\n",
    "np.save('a.npy', a)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## savetxt\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.savetxt.html\n",
    "\n",
    "With the help of this function, numerical array can be saved in a text file. The function takes two positional arguments, the name of the output file, and the array, and also implements the `comments='#'`\n",
    "`delimiter=' '`, the `header=''`, and `footer=''` keyword arguments. The input is treated as of type `float`, i.e., the output is always in the floating point representation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-01-28T18:56:06.933706Z",
     "start_time": "2022-01-28T18:56:06.872547Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.000000000000000 1.000000000000000 2.000000000000000 3.000000000000000\n",
      "4.000000000000000 5.000000000000000 6.000000000000000 7.000000000000000\n",
      "8.000000000000000 9.000000000000000 10.000000000000000 11.000000000000000\n",
      "\n",
      "!col1;col2;col3;col4\n",
      "0.000000000000000;1.000000000000000;2.000000000000000;3.000000000000000\n",
      "4.000000000000000;5.000000000000000;6.000000000000000;7.000000000000000\n",
      "8.000000000000000;9.000000000000000;10.000000000000000;11.000000000000000\n",
      "!saved data\n",
      "\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array(range(12), dtype=np.uint8).reshape((3, 4))\n",
    "np.savetxt('savetxt.dat', a)\n",
    "\n",
    "with open('savetxt.dat', 'r') as fin:\n",
    "    print(fin.read())\n",
    "    \n",
    "np.savetxt('savetxt.dat', a, \n",
    "           comments='!', \n",
    "           delimiter=';', \n",
    "           header='col1;col2;col3;col4', \n",
    "           footer='saved data')\n",
    "\n",
    "with open('savetxt.dat', 'r') as fin:\n",
    "    print(fin.read())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## size\n",
    "\n",
    "The function takes a single positional argument, and an optional keyword argument, `axis`, with a default value of `None`, and returns the size of an array along that axis. If `axis` is `None`, the total length of the array (the product of the elements of its shape) is returned."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-01-15T08:50:57.254168Z",
     "start_time": "2022-01-15T08:50:57.245772Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "array([[1.0, 1.0, 1.0],\n",
      "       [1.0, 1.0, 1.0]], dtype=float64)\n",
      "size(a, axis=0):  2\n",
      "size(a, axis=1):  3\n",
      "size(a, axis=None):  6\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.ones((2, 3))\n",
    "\n",
    "print(a)\n",
    "print('size(a, axis=0): ', np.size(a, axis=0))\n",
    "print('size(a, axis=1): ', np.size(a, axis=1))\n",
    "print('size(a, axis=None): ', np.size(a, axis=None))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## sort\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.sort.html\n",
    "\n",
    "The sort function takes an ndarray, and sorts its elements in ascending order along the specified axis using a heap sort algorithm. As opposed to the `.sort()` method discussed earlier, this function creates a copy of its input before sorting, and at the end, returns this copy. Sorting takes place in place, without auxiliary storage. The `axis` keyword argument takes on the possible values of -1 (the last axis, in `ulab` equivalent to the second axis, and this also happens to be the default value), 0, 1, or `None`. The first three cases are identical to those in [diff](#diff), while the last one flattens the array before sorting. \n",
    "\n",
    "If descending order is required, the result can simply be `flip`ped, see [flip](#flip).\n",
    "\n",
    "**WARNING:** `numpy` defines the `kind`, and `order` keyword arguments that are not implemented here. The function in `ulab` always uses heap sort, and since `ulab` does not have the concept of data fields, the `order` keyword argument would have no meaning."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T16:32:07.748972Z",
     "start_time": "2021-01-13T16:32:07.730498Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "a:\n",
      " array([[1.0, 12.0, 3.0, 0.0],\n",
      "       [5.0, 3.0, 4.0, 1.0],\n",
      "       [9.0, 11.0, 1.0, 8.0],\n",
      "       [7.0, 10.0, 0.0, 1.0]], dtype=float64)\n",
      "\n",
      "a sorted along vertical axis:\n",
      " array([[1.0, 3.0, 0.0, 0.0],\n",
      "       [5.0, 10.0, 1.0, 1.0],\n",
      "       [7.0, 11.0, 3.0, 1.0],\n",
      "       [9.0, 12.0, 4.0, 8.0]], dtype=float64)\n",
      "\n",
      "a sorted along horizontal axis:\n",
      " array([[0.0, 1.0, 3.0, 12.0],\n",
      "       [1.0, 3.0, 4.0, 5.0],\n",
      "       [1.0, 8.0, 9.0, 11.0],\n",
      "       [0.0, 1.0, 7.0, 10.0]], dtype=float64)\n",
      "\n",
      "flattened a sorted:\n",
      " array([0.0, 0.0, 1.0, ..., 10.0, 11.0, 12.0], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([[1, 12, 3, 0], [5, 3, 4, 1], [9, 11, 1, 8], [7, 10, 0, 1]], dtype=np.float)\n",
    "print('\\na:\\n', a)\n",
    "b = np.sort(a, axis=0)\n",
    "print('\\na sorted along vertical axis:\\n', b)\n",
    "\n",
    "c = np.sort(a, axis=1)\n",
    "print('\\na sorted along horizontal axis:\\n', c)\n",
    "\n",
    "c = np.sort(a, axis=None)\n",
    "print('\\nflattened a sorted:\\n', c)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Heap sort requires $\\sim N\\log N$ operations, and notably, the worst case costs only 20% more time than the average. In order to get an order-of-magnitude estimate, we will take the sine of 1000 uniformly spaced numbers between 0, and two pi, and sort them:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%micropython -pyboard 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "@timeit\n",
    "def sort_time(array):\n",
    "    return nup.sort(array)\n",
    "\n",
    "b = np.sin(np.linspace(0, 6.28, num=1000))\n",
    "print('b: ', b)\n",
    "sort_time(b)\n",
    "print('\\nb sorted:\\n', b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## sort_complex\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.sort_complex.html\n",
    "\n",
    "If the firmware was compiled with complex support, the functions sorts the input array first according to its real part, and then the imaginary part. The input must be a one-dimensional array. The output is always of `dtype` complex, even if the input was real integer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2022-01-07T19:36:15.750029Z",
     "start_time": "2022-01-07T19:36:15.732210Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:\t\t\t array([5, 4, 3, 2, 1], dtype=int16)\n",
      "sort_complex(a):\t array([1.0+0.0j, 2.0+0.0j, 3.0+0.0j, 4.0+0.0j, 5.0+0.0j], dtype=complex)\n",
      "\n",
      "b:\t\t\t array([5.0+0.0j, 4.0+3.0j, 4.0-2.0j, 0.0+0.0j, 0.0+1.0j], dtype=complex)\n",
      "sort_complex(b):\t array([0.0+0.0j, 0.0+1.0j, 4.0-2.0j, 4.0+3.0j, 5.0+0.0j], dtype=complex)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([5, 4, 3, 2, 1], dtype=np.int16)\n",
    "print('a:\\t\\t\\t', a)\n",
    "print('sort_complex(a):\\t', np.sort_complex(a))\n",
    "print()\n",
    "\n",
    "b = np.array([5, 4+3j, 4-2j, 0, 1j], dtype=np.complex)\n",
    "print('b:\\t\\t\\t', b)\n",
    "print('sort_complex(b):\\t', np.sort_complex(b))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## std\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.std.html\n",
    "\n",
    "If the axis keyword is not specified, it assumes the default value of `None`, and returns the result of the computation for the flattened array. Otherwise, the calculation is along the given axis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T16:14:54.051061Z",
     "start_time": "2021-01-13T16:14:54.029924Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a: \n",
      " array([[1.0, 2.0, 3.0],\n",
      "       [4.0, 5.0, 6.0],\n",
      "       [7.0, 8.0, 9.0]], dtype=float64)\n",
      "sum, flat array:  2.581988897471611\n",
      "std, vertical:  array([2.449489742783178, 2.449489742783178, 2.449489742783178], dtype=float64)\n",
      "std, horizonal:  array([0.8164965809277261, 0.8164965809277261, 0.8164965809277261], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
    "print('a: \\n', a)\n",
    "print('sum, flat array: ', np.std(a))\n",
    "print('std, vertical: ', np.std(a, axis=0))\n",
    "print('std, horizonal: ', np.std(a, axis=1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## sum\n",
    "\n",
    "`numpy`: https://docs.scipy.org/doc/numpy/reference/generated/numpy.sum.html\n",
    "\n",
    "If the axis keyword is not specified, it assumes the default value of `None`, and returns the result of the computation for the flattened array. Otherwise, the calculation is along the given axis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T16:14:34.576723Z",
     "start_time": "2021-01-13T16:14:34.556304Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a: \n",
      " array([[1.0, 2.0, 3.0],\n",
      "       [4.0, 5.0, 6.0],\n",
      "       [7.0, 8.0, 9.0]], dtype=float64)\n",
      "sum, flat array:  45.0\n",
      "sum, horizontal:  array([6.0, 15.0, 24.0], dtype=float64)\n",
      "std, vertical:  array([12.0, 15.0, 18.0], dtype=float64)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n",
    "print('a: \\n', a)\n",
    "\n",
    "print('sum, flat array: ', np.sum(a))\n",
    "print('sum, horizontal: ', np.sum(a, axis=1))\n",
    "print('std, vertical: ', np.sum(a, axis=0))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## trace\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.trace.html\n",
    "\n",
    "The `trace` function returns the sum of the diagonal elements of a square matrix. If the input argument is not a square matrix, an exception will be raised.\n",
    "\n",
    "The scalar so returned will inherit the type of the input array, i.e., integer arrays have integer trace, and floating point arrays a floating point trace."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-02-13T08:30:25.211965Z",
     "start_time": "2021-02-13T08:30:25.195102Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a:  array([[25, 15, -5],\n",
      "       [15, 18, 0],\n",
      "       [-5, 0, 11]], dtype=int8)\n",
      "\n",
      "trace of a:  54\n",
      "====================\n",
      "b:  array([[25.0, 15.0, -5.0],\n",
      "       [15.0, 18.0, 0.0],\n",
      "       [-5.0, 0.0, 11.0]], dtype=float64)\n",
      "\n",
      "trace of b:  54.0\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "a = np.array([[25, 15, -5], [15, 18,  0], [-5,  0, 11]], dtype=np.int8)\n",
    "print('a: ', a)\n",
    "print('\\ntrace of a: ', np.trace(a))\n",
    "\n",
    "b = np.array([[25, 15, -5], [15, 18,  0], [-5,  0, 11]], dtype=np.float)\n",
    "\n",
    "print('='*20 + '\\nb: ', b)\n",
    "print('\\ntrace of b: ', np.trace(b))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## trapz\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.trapz.html\n",
    "\n",
    "The function takes one or two one-dimensional `ndarray`s, and integrates the dependent values (`y`) using the trapezoidal rule. If the independent variable (`x`) is given, that is taken as the sample points corresponding to `y`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-01-13T16:03:42.566302Z",
     "start_time": "2021-01-13T16:03:42.545630Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "x:  array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0], dtype=float64)\n",
      "y:  array([0.0, 1.0, 4.0, 9.0, 16.0, 25.0, 36.0, 49.0, 64.0, 81.0], dtype=float64)\n",
      "============================\n",
      "integral of y:  244.5\n",
      "integral of y at x:  244.5\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "x = np.linspace(0, 9, num=10)\n",
    "y = x*x\n",
    "\n",
    "print('x: ',  x)\n",
    "print('y: ',  y)\n",
    "print('============================')\n",
    "print('integral of y: ', np.trapz(y))\n",
    "print('integral of y at x: ', np.trapz(y, x=x))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## where\n",
    "\n",
    "`numpy`: https://numpy.org/doc/stable/reference/generated/numpy.where.html\n",
    "\n",
    "The function takes three positional arguments, `condition`, `x`, and `y`, and returns a new `ndarray`, whose values are taken from either `x`, or `y`, depending on the truthness of `condition`. The three arguments are broadcast together, and the function raises a `ValueError` exception, if broadcasting is not possible.\n",
    "\n",
    "The function is implemented for `ndarray`s only: other iterable types can be passed after casting them to an `ndarray` by calling the `array` constructor.\n",
    "\n",
    "If the `dtype`s of `x`, and `y` differ, the output is upcast as discussed earlier.  \n",
    "\n",
    "Note that the `condition` is expanded into an Boolean `ndarray`. This means that the storage required to hold the condition should be taken into account, whenever the function is called."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The following example returns an `ndarray`  of length 4, with 1 at positions, where `condition` is smaller than 3, and with -1 otherwise."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-23T16:18:14.396840Z",
     "start_time": "2021-03-23T16:18:14.385134Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "array([1, 1, -1, -1], dtype=int16)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "condition = np.array([1, 2, 3, 4], dtype=np.uint8)\n",
    "print(np.where(condition < 3, 1, -1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next snippet shows, how values from two arrays can be fed into the output:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2021-03-23T16:15:29.954224Z",
     "start_time": "2021-03-23T16:15:29.937205Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "array([11, 22, 3, 4], dtype=uint8)\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "%%micropython -unix 1\n",
    "\n",
    "\n",
    "from ulab import numpy as np\n",
    "\n",
    "condition = np.array([1, 2, 3, 4], dtype=np.uint8)\n",
    "x = np.array([11, 22, 33, 44], dtype=np.uint8)\n",
    "y = np.array([1, 2, 3, 4], dtype=np.uint8)\n",
    "print(np.where(condition < 3, x, y))"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3.8.5 ('base')",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.13"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {
    "height": "calc(100% - 180px)",
    "left": "10px",
    "top": "150px",
    "width": "382.797px"
   },
   "toc_section_display": true,
   "toc_window_display": true
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  },
  "vscode": {
   "interpreter": {
    "hash": "9e4ec6f642f986afcc9e252c165e44859a62defc5c697cae6f82c2943465ec10"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
